home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / a_utils / _archvrs / unix / tar_110.lha / tar-1.10 / diffarch.c < prev    next >
C/C++ Source or Header  |  1990-12-13  |  15KB  |  687 lines

  1. /* Diff files from a tar archive.
  2.    Copyright (C) 1988 Free Software Foundation
  3.  
  4. This file is part of GNU Tar.
  5.  
  6. GNU Tar is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. GNU Tar is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU Tar; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /*
  21.  * Diff files from a tar archive.
  22.  *
  23.  * Written 30 April 1987 by John Gilmore, ihnp4!hoptoad!gnu.
  24.  *
  25.  * @(#) diffarch.c 1.10 87/11/11 - gnu
  26.  */
  27.  
  28. #include <stdio.h>
  29. #include <errno.h>
  30. #include <sys/types.h>
  31. #include <sys/stat.h>
  32.  
  33. #ifdef BSD42
  34. #include <sys/file.h>
  35. #endif
  36.  
  37. #ifndef MSDOS
  38. #include <sys/ioctl.h>
  39. #if !defined(USG) || defined(HAVE_MTIO)
  40. #include <sys/mtio.h>
  41. #endif
  42. #endif
  43.  
  44. #ifdef USG
  45. #include <fcntl.h>
  46. #endif
  47.  
  48. /* Some systems don't have these #define's -- we fake it here. */
  49. #ifndef O_RDONLY
  50. #define    O_RDONLY    0
  51. #endif
  52. #ifndef    O_NDELAY
  53. #define    O_NDELAY    0
  54. #endif
  55.  
  56. #ifndef S_IFLNK
  57. #define lstat stat
  58. #endif
  59.  
  60. extern int errno;            /* From libc.a */
  61. extern char *valloc();            /* From libc.a */
  62.  
  63. #include "tar.h"
  64. #include "port.h"
  65. #include "rmt.h"
  66.  
  67. extern union record *head;        /* Points to current tape header */
  68. extern struct stat hstat;        /* Stat struct corresponding */
  69. extern int head_standard;        /* Tape header is in ANSI format */
  70.  
  71. extern void print_header();
  72. extern void skip_file();
  73. extern void skip_extended_headers();
  74.  
  75. extern FILE *msg_file;
  76.  
  77. int now_verifying = 0;        /* Are we verifying at the moment? */
  78.  
  79. int    diff_fd;        /* Descriptor of file we're diffing */
  80.  
  81. char    *diff_buf = 0;        /* Pointer to area for reading
  82.                        file contents into */
  83.  
  84. char    *diff_dir;        /* Directory contents for LF_DUMPDIR */
  85.  
  86. int different = 0;
  87.  
  88. /*struct sp_array *sparsearray;
  89. int         sp_ar_size = 10;*/
  90. /*
  91.  * Initialize for a diff operation
  92.  */
  93. diff_init()
  94. {
  95.  
  96.     /*NOSTRICT*/
  97.     diff_buf = (char *) valloc((unsigned)blocksize);
  98.     if (!diff_buf) {
  99.         msg("could not allocate memory for diff buffer of %d bytes",
  100.             blocksize);
  101.         exit(EX_ARGSBAD);
  102.     }
  103. }
  104.  
  105. /*
  106.  * Diff a file against the archive.
  107.  */
  108. void
  109. diff_archive()
  110. {
  111.     register char *data;
  112.     int check, namelen;
  113.     int err;
  114.     long offset;
  115.     struct stat filestat;
  116.     int compare_chunk();
  117.     int compare_dir();
  118. #ifndef __MSDOS__
  119.     dev_t    dev;
  120.     ino_t    ino;
  121. #endif
  122.     char *get_dir_contents();
  123.     long from_oct();
  124.     long lseek();
  125.  
  126.     errno = EPIPE;            /* FIXME, remove perrors */
  127.  
  128.     saverec(&head);            /* Make sure it sticks around */
  129.     userec(head);            /* And go past it in the archive */
  130.     decode_header(head, &hstat, &head_standard, 1);    /* Snarf fields */
  131.  
  132.     /* Print the record from 'head' and 'hstat' */
  133.     if (f_verbose) {
  134.         if(now_verifying)
  135.             fprintf(msg_file,"Verify ");
  136.         print_header();
  137.     }
  138.  
  139.     switch (head->header.linkflag) {
  140.  
  141.     default:
  142.         msg("Unknown file type '%c' for %s, diffed as normal file",
  143.             head->header.linkflag, head->header.name);
  144.         /* FALL THRU */
  145.  
  146.     case LF_OLDNORMAL:
  147.     case LF_NORMAL:
  148.     case LF_SPARSE:
  149.     case LF_CONTIG:
  150.         /*
  151.          * Appears to be a file.
  152.          * See if it's really a directory.
  153.          */
  154.         namelen = strlen(head->header.name)-1;
  155.         if (head->header.name[namelen] == '/')
  156.             goto really_dir;
  157.  
  158.         
  159.         if(do_stat(&filestat)) {
  160.             if (head->header.isextended)
  161.                 skip_extended_headers();
  162.             skip_file((long)hstat.st_size);
  163.             different++;
  164.             goto quit;
  165.         }
  166.  
  167.         if ((filestat.st_mode & S_IFMT) != S_IFREG) {
  168.             fprintf(msg_file, "%s: not a regular file\n",
  169.                 head->header.name);
  170.             skip_file((long)hstat.st_size);
  171.             different++;
  172.             goto quit;
  173.         }
  174.  
  175.         filestat.st_mode &= ~S_IFMT;
  176.         if (filestat.st_mode != hstat.st_mode)
  177.             sigh("mode");
  178.         if (filestat.st_uid  != hstat.st_uid)
  179.             sigh("uid");
  180.         if (filestat.st_gid  != hstat.st_gid)
  181.             sigh("gid");
  182.         if (filestat.st_mtime != hstat.st_mtime)
  183.             sigh("mod time");
  184.         if (head->header.linkflag != LF_SPARSE &&
  185.                 filestat.st_size != hstat.st_size) {
  186.             sigh("size");
  187.             skip_file((long)hstat.st_size);
  188.             goto quit;
  189.         }
  190.  
  191.         diff_fd = open(head->header.name, O_NDELAY|O_RDONLY);
  192.  
  193.         if (diff_fd < 0 && !f_absolute_paths) {
  194.             char tmpbuf[NAMSIZ+2];
  195.  
  196.             tmpbuf[0]='/';
  197.             strcpy(&tmpbuf[1],head->header.name);
  198.             diff_fd=open(tmpbuf, O_NDELAY|O_RDONLY);
  199.         }
  200.         if (diff_fd < 0) {
  201.             msg_perror("cannot open %s",head->header.name);
  202.             if (head->header.isextended)
  203.                 skip_extended_headers();
  204.             skip_file((long)hstat.st_size);
  205.             different++;
  206.             goto quit;
  207.         }
  208.         /*
  209.          * Need to treat sparse files completely differently here.
  210.          */
  211.         if (head->header.linkflag == LF_SPARSE)
  212.             diff_sparse_files(hstat.st_size);
  213.         else 
  214.             wantbytes((long)(hstat.st_size),compare_chunk);
  215.  
  216.         check = close(diff_fd);
  217.         if (check < 0)
  218.             msg_perror("Error while closing %s",head->header.name);
  219.  
  220.     quit:
  221.         break;
  222.  
  223. #ifndef __MSDOS__
  224.     case LF_LINK:
  225.         if(do_stat(&filestat))
  226.             break;
  227.         dev = filestat.st_dev;
  228.         ino = filestat.st_ino;
  229.         err = stat(head->header.linkname, &filestat);
  230.         if (err < 0) {
  231.             if (errno==ENOENT) {
  232.                 fprintf(msg_file, "%s: does not exist\n",head->header.name);
  233.             } else {
  234.                 msg_perror("cannot stat file %s",head->header.name);
  235.             }
  236.             different++;
  237.             break;
  238.         }
  239.         if(filestat.st_dev!=dev || filestat.st_ino!=ino) {
  240.             fprintf(msg_file, "%s not linked to %s\n",head->header.name,head->header.linkname);
  241.             break;
  242.         }
  243.         break;
  244. #endif
  245.  
  246. #ifdef S_IFLNK
  247.     case LF_SYMLINK:
  248.     {
  249.         char linkbuf[NAMSIZ+3];
  250.         check = readlink(head->header.name, linkbuf,
  251.                  (sizeof linkbuf)-1);
  252.         
  253.         if (check < 0) {
  254.             if (errno == ENOENT) {
  255.                 fprintf(msg_file,
  256.                     "%s: no such file or directory\n",
  257.                     head->header.name);
  258.             } else {
  259.                 msg_perror("cannot read link %s",head->header.name);
  260.             }
  261.             different++;
  262.             break;
  263.         }
  264.  
  265.         linkbuf[check] = '\0';    /* Null-terminate it */
  266.         if (strncmp(head->header.linkname, linkbuf, check) != 0) {
  267.             fprintf(msg_file, "%s: symlink differs\n",
  268.                 head->header.linkname);
  269.             different++;
  270.         }
  271.     }
  272.         break;
  273. #endif
  274.  
  275.     case LF_CHR:
  276.         hstat.st_mode |= S_IFCHR;
  277.         goto check_node;
  278.  
  279. #ifdef S_IFBLK
  280.     /* If local system doesn't support block devices, use default case */
  281.     case LF_BLK:
  282.         hstat.st_mode |= S_IFBLK;
  283.         goto check_node;
  284. #endif
  285.  
  286. #ifdef S_IFIFO
  287.     /* If local system doesn't support FIFOs, use default case */
  288.     case LF_FIFO:
  289.         hstat.st_mode |= S_IFIFO;
  290.         hstat.st_rdev = 0;        /* FIXME, do we need this? */
  291.         goto check_node;
  292. #endif
  293.  
  294.     check_node:
  295.         /* FIXME, deal with umask */
  296.         if(do_stat(&filestat))
  297.             break;
  298.         if(hstat.st_rdev != filestat.st_rdev) {
  299.             fprintf(msg_file, "%s: device numbers changed\n", head->header.name);
  300.             different++;
  301.             break;
  302.         }
  303.         if(hstat.st_mode != filestat.st_mode) {
  304.             fprintf(msg_file, "%s: mode or device-type changed\n", head->header.name);
  305.             different++;
  306.             break;
  307.         }
  308.         break;
  309.  
  310.     case LF_DUMPDIR:
  311.         data=diff_dir=get_dir_contents(head->header.name,0);
  312.         wantbytes((long)(hstat.st_size),compare_dir);
  313.         free(data);
  314.         /* FALL THROUGH */
  315.  
  316.     case LF_DIR:
  317.         /* Check for trailing / */
  318.         namelen = strlen(head->header.name)-1;
  319.     really_dir:
  320.         while (namelen && head->header.name[namelen] == '/')
  321.             head->header.name[namelen--] = '\0';    /* Zap / */
  322.  
  323.         if(do_stat(&filestat))
  324.             break;
  325.         if((filestat.st_mode&S_IFMT)!=S_IFDIR) {
  326.             fprintf(msg_file, "%s is no longer a directory\n",head->header.name);
  327.             different++;
  328.             break;
  329.         }
  330.         if((filestat.st_mode&~S_IFMT) != hstat.st_mode)
  331.             sigh("mode");
  332.         break;
  333.  
  334.     case LF_VOLHDR:
  335.         break;
  336.  
  337.     case LF_MULTIVOL:
  338.         namelen = strlen(head->header.name)-1;
  339.         if (head->header.name[namelen] == '/')
  340.             goto really_dir;
  341.  
  342.         if(do_stat(&filestat))
  343.             break;
  344.  
  345.         if ((filestat.st_mode & S_IFMT) != S_IFREG) {
  346.             fprintf(msg_file, "%s: not a regular file\n",
  347.                 head->header.name);
  348.             skip_file((long)hstat.st_size);
  349.             different++;
  350.             break;
  351.         }
  352.  
  353.         filestat.st_mode &= ~S_IFMT;
  354.         offset = from_oct(1+12, head->header.offset);
  355.         if (filestat.st_size != hstat.st_size + offset) {
  356.             sigh("size");
  357.             skip_file((long)hstat.st_size);
  358.             different++;
  359.             break;
  360.         }
  361.  
  362.         diff_fd = open(head->header.name, O_NDELAY|O_RDONLY);
  363.  
  364.         if (diff_fd < 0) {
  365.             msg_perror("cannot open file %s",head->header.name);
  366.             skip_file((long)hstat.st_size);
  367.             different++;
  368.             break;
  369.         }
  370.         err = lseek(diff_fd, offset, 0);
  371.         if(err!=offset) {
  372.             msg_perror("cannot seek to %ld in file %s",offset,head->header.name);
  373.             different++;
  374.             break;
  375.         }
  376.  
  377.         wantbytes((long)(hstat.st_size),compare_chunk);
  378.  
  379.         check = close(diff_fd);
  380.         if (check < 0) {
  381.             msg_perror("Error while closing %s",head->header.name);
  382.         }
  383.         break;
  384.  
  385.     }
  386.  
  387.     /* We don't need to save it any longer. */
  388.     saverec((union record **) 0);    /* Unsave it */
  389. }
  390.  
  391. int
  392. compare_chunk(bytes,buffer)
  393. long bytes;
  394. char *buffer;
  395. {
  396.     int err;
  397.  
  398.     err=read(diff_fd,diff_buf,bytes);
  399.     if(err!=bytes) {
  400.         if(err<0) {
  401.             msg_perror("can't read %s",head->header.name);
  402.         } else {
  403.             fprintf(msg_file,"%s: could only read %d of %d bytes\n",head->header.name,err,bytes);
  404.         }
  405.         different++;
  406.         return -1;
  407.     }
  408.     if(bcmp(buffer,diff_buf,bytes)) {
  409.         fprintf(msg_file, "%s: data differs\n",head->header.name);
  410.         different++;
  411.         return -1;
  412.     }
  413.     return 0;
  414. }
  415.  
  416. int
  417. compare_dir(bytes,buffer)
  418. long bytes;
  419. char *buffer;
  420. {
  421.     if(bcmp(buffer,diff_dir,bytes)) {
  422.         fprintf(msg_file, "%s: data differs\n",head->header.name);
  423.         different++;
  424.         return -1;
  425.     }
  426.     diff_dir+=bytes;
  427.     return 0;
  428. }
  429.  
  430. /*
  431.  * Sigh about something that differs.
  432.  */
  433. sigh(what)
  434.     char *what;
  435. {
  436.  
  437.     fprintf(msg_file, "%s: %s differs\n",
  438.         head->header.name, what);
  439. }
  440.  
  441. verify_volume()
  442. {
  443.     int status;
  444. #ifdef MTIOCTOP
  445.     struct mtop t;
  446.     int er;
  447. #endif
  448.  
  449.     if(!diff_buf)
  450.         diff_init();
  451. #ifdef MTIOCTOP
  452.     t.mt_op = MTBSF;
  453.     t.mt_count = 1;
  454.     if((er=rmtioctl(archive,MTIOCTOP,&t))<0) {
  455.         if(errno!=EIO || (er=rmtioctl(archive,MTIOCTOP,&t))<0) {
  456. #endif
  457.             if(rmtlseek(archive,0L,0)!=0) {
  458.                 /* Lseek failed.  Try a different method */
  459.                 msg_perror("Couldn't rewind archive file for verify");
  460.                 return;
  461.             }
  462. #ifdef MTIOCTOP
  463.         }
  464.     }
  465. #endif
  466.     ar_reading=1;
  467.     now_verifying = 1;
  468.     fl_read();
  469.     for(;;) {
  470.         status = read_header();
  471.         if(status==0) {
  472.             unsigned n;
  473.  
  474.             n=0;
  475.             do {
  476.                 n++;
  477.                 status=read_header();
  478.             } while(status==0);
  479.             msg("VERIFY FAILURE: %d invalid header%s detected!",n,n==1?"":"s");
  480.         }
  481.         if(status==2 || status==EOF)
  482.             break;
  483.         diff_archive();
  484.     }
  485.     ar_reading=0;
  486.     now_verifying = 0;
  487.  
  488. }
  489.  
  490. int do_stat(statp)
  491. struct stat *statp;
  492. {
  493.     int err;
  494.  
  495.     err = f_follow_links ? stat(head->header.name, statp) : lstat(head->header.name, statp);
  496.     if (err < 0) {
  497.         if (errno==ENOENT) {
  498.             fprintf(msg_file, "%s: does not exist\n",head->header.name);
  499.         } else
  500.             msg_perror("can't stat file %s",head->header.name);
  501. /*        skip_file((long)hstat.st_size);
  502.         different++;*/
  503.         return 1;
  504.     } else
  505.         return 0;
  506. }
  507.  
  508. /*
  509.  * JK
  510.  * Diff'ing a sparse file with its counterpart on the tar file is a 
  511.  * bit of a different story than a normal file.  First, we must know
  512.  * what areas of the file to skip through, i.e., we need to contruct
  513.  * a sparsearray, which will hold all the information we need.  We must
  514.  * compare small amounts of data at a time as we find it.  
  515.  */
  516.  
  517. diff_sparse_files(filesize)
  518. int    filesize;
  519.  
  520. {
  521.     int        sparse_ind = 0;
  522.     char        *buf;
  523.     int        buf_size = RECORDSIZE;
  524.     union record     *datarec;    
  525.     int        err;
  526.     long        numbytes;
  527.     int        amt_read = 0;
  528.     int        size = filesize;
  529.  
  530.     buf = (char *) malloc(buf_size * sizeof (char));
  531.     
  532.     fill_in_sparse_array();
  533.     
  534.  
  535.     while (size > 0) {
  536.         datarec = findrec();
  537.         if (!sparsearray[sparse_ind].numbytes)
  538.             break;
  539.  
  540.         /*
  541.          * 'numbytes' is nicer to write than
  542.          * 'sparsearray[sparse_ind].numbytes' all the time ...
  543.          */
  544.         numbytes = sparsearray[sparse_ind].numbytes;
  545.         
  546.         lseek(diff_fd, sparsearray[sparse_ind].offset, 0);
  547.         /*
  548.          * take care to not run out of room in our buffer
  549.          */
  550.         while (buf_size < numbytes) {
  551.             buf = (char *) realloc(buf, buf_size * 2 * sizeof(char));
  552.             buf_size *= 2;
  553.         }
  554.         while (numbytes > RECORDSIZE) {
  555.             if ((err = read(diff_fd, buf, RECORDSIZE)) != RECORDSIZE) {
  556.                  if (err < 0) 
  557.                     msg_perror("can't read %s", head->header.name);
  558.                 else
  559.                     fprintf(msg_file, "%s: could only read %d of %d bytes\n", 
  560.                         err, numbytes);
  561.                 break;
  562.             }
  563.             if (bcmp(buf, datarec->charptr, RECORDSIZE)) {
  564.                 different++;
  565.                 break;
  566.             }
  567.             numbytes -= err;
  568.             size -= err;
  569.             userec(datarec);
  570.             datarec = findrec();
  571.         }
  572.         if ((err = read(diff_fd, buf, numbytes)) != numbytes) {
  573.              if (err < 0) 
  574.                 msg_perror("can't read %s", head->header.name);
  575.             else
  576.                 fprintf(msg_file, "%s: could only read %d of %d bytes\n", 
  577.                         err, numbytes);
  578.             break;
  579.         }
  580.  
  581.         if (bcmp(buf, datarec->charptr, numbytes)) {
  582.             different++;
  583.             break;
  584.         }
  585. /*        amt_read += numbytes;
  586.         if (amt_read >= RECORDSIZE) {
  587.             amt_read = 0;
  588.             userec(datarec);
  589.             datarec = findrec();
  590.         }*/
  591.         userec(datarec);
  592.         sparse_ind++;
  593.         size -= numbytes;
  594.     }
  595.     /* 
  596.      * if the number of bytes read isn't the
  597.      * number of bytes supposedly in the file,
  598.      * they're different
  599.      */
  600. /*    if (amt_read != filesize)
  601.         different++;*/
  602.     userec(datarec);
  603.     free(sparsearray);
  604.     if (different)
  605.         fprintf(msg_file, "%s: data differs\n", head->header.name);
  606.  
  607. }
  608.  
  609. /*
  610.  * JK
  611.  * This routine should be used more often than it is ... look into
  612.  * that.  Anyhow, what it does is translate the sparse information
  613.  * on the header, and in any subsequent extended headers, into an
  614.  * array of structures with true numbers, as opposed to character
  615.  * strings.  It simply makes our life much easier, doing so many
  616.  * comparisong and such.
  617.  */
  618. fill_in_sparse_array()
  619. {
  620.     int     ind;
  621.  
  622.     /*
  623.      * allocate space for our scratch space; it's initially
  624.      * 10 elements long, but can change in this routine if
  625.      * necessary
  626.      */
  627.     sp_array_size = 10;
  628.     sparsearray = (struct sp_array *) malloc(sp_array_size * sizeof(struct sp_array));
  629.  
  630.     /*
  631.      * there are at most five of these structures in the header
  632.      * itself; read these in first
  633.      */
  634.     for (ind = 0; ind < SPARSE_IN_HDR; ind++) {
  635.         if (!head->header.sp[ind].numbytes)
  636.             break;
  637.         sparsearray[ind].offset =
  638.             from_oct(1+12, head->header.sp[ind].offset);
  639.         sparsearray[ind].numbytes =
  640.             from_oct(1+12, head->header.sp[ind].numbytes);
  641.     }
  642.     /*
  643.      * if the header's extended, we gotta read in exhdr's till
  644.      * we're done
  645.      */
  646.     if (head->header.isextended) {
  647.          /* how far into the sparsearray we are 'so far' */
  648.         static int so_far_ind = SPARSE_IN_HDR;    
  649.         union record *exhdr;
  650.            
  651.         for (;;) {
  652.         exhdr = findrec();
  653.         for (ind = 0; ind < SPARSE_EXT_HDR; ind++) {
  654.             if (ind+so_far_ind > sp_array_size-1) {
  655.                 /*
  656.                   * we just ran out of room in our
  657.                  *  scratch area - realloc it
  658.                   */
  659.                 sparsearray = (struct sp_array *)
  660.                     realloc(sparsearray, 
  661.                         sp_array_size*2*sizeof(struct sp_array));
  662.                 sp_array_size *= 2;
  663.             }
  664.             /*
  665.              * convert the character strings into longs
  666.              */
  667.             sparsearray[ind+so_far_ind].offset = 
  668.                 from_oct(1+12, exhdr->ext_hdr.sp[ind].offset);
  669.             sparsearray[ind+so_far_ind].numbytes =
  670.                 from_oct(1+12, exhdr->ext_hdr.sp[ind].numbytes);
  671.         }
  672.         /* 
  673.          * if this is the last extended header for this
  674.          * file, we can stop
  675.          */
  676.         if (!exhdr->ext_hdr.isextended)
  677.             break;
  678.         else {
  679.             so_far_ind += SPARSE_EXT_HDR;
  680.             userec(exhdr);
  681.         }
  682.         }
  683.         /* be sure to skip past the last one  */
  684.         userec(exhdr);
  685.     }
  686. }
  687.